package com.dm.cloud.databases.mutimysql;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.dm.cloud.utils.SpringContextUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
  * @Description: dataSource参数配置类
  * @return
  * @throws
  */
@Log4j2
@Configuration
@ConditionalOnProperty(prefix = "custom.datasource.muti", name = "enable", havingValue = "true")
@EnableConfigurationProperties(DruidProperties.class)
public class DataSourceConfig {

	private final String DATAS_PRE_FIX = "custom.datasource.muti.datas";

	private final String DATAS_PRE_FIX_DOT = "custom.datasource.muti.datas.";

	private final String DOT_ISBASE = ".isBase";

	private SpringContextUtils springContextUtils;//spring上下文

	private DruidProperties druidProperties;//Durid属性配置

	private List<String> dataNames = new ArrayList<>();//数据源名称列表

	private Map<Object,Object> dataSources = new HashMap<>();//数据源列表

	private DataSource defaultDataSouce ;//默认数据库

	/**
	 * 构造器
	 * 通过构造器注入springContextUtils和druidProperties
	 * @param springContextUtils
	 * @param druidProperties
	 */
	public DataSourceConfig(SpringContextUtils springContextUtils,DruidProperties druidProperties){
		this.springContextUtils =springContextUtils ;
		this.druidProperties = druidProperties;
	}

	/**
	 * 配置多数据源和默认数据源
	 * @return
	 */
	@Bean(name = "dynamicDataSource")
	@Primary
	public DataSource dataSource(){

		//获取数据库配置
		List<String> druidProp = springContextUtils.getPropertyList(DATAS_PRE_FIX);

		//解析配置 排除关键字段
		dataNames = druidProp.stream()
				.map(e->e.split("custom\\.datasource\\.muti\\.datas\\.")[1])//截取custom.datasource.后面的字符串
				.map(e->e.split("\\.")[0])//根据“.”分割获取数据库的名称属性
				.distinct()//去重
				.collect(Collectors.toList());

		//初始化数据源信息
		for (String dataName : dataNames) {
			String cloudBaseUrl =getDataProp(dataName,"url");
			String cloudBasesername =getDataProp(dataName,"username");
			String cloudBasePassword =getDataProp(dataName,"password");
			String cloudBaseDriverClassName =getDataProp(dataName,"driverClassName");
			String cloudBaseValidationQuery =getDataProp(dataName,"validationQuery");

			DataSource dataSource =initDruidDataSource(cloudBaseUrl,cloudBasesername,cloudBasePassword,cloudBaseDriverClassName,cloudBaseValidationQuery);

			if(springContextUtils.containProperty(DATAS_PRE_FIX_DOT+dataName+DOT_ISBASE,true)
					&& "true".equals(springContextUtils.getProperty(DATAS_PRE_FIX_DOT+dataName+DOT_ISBASE).toString())){
				defaultDataSouce = dataSource;
			}

			//当默认数据源为空时 设置第一个数据库为默认数据源
			if(null == defaultDataSouce){
				defaultDataSouce = dataSource;
			}

			dataSources.put(dataName,dataSource);
		}

		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		//设置默认数据源
		dynamicDataSource.setDefaultTargetDataSource(defaultDataSouce);
		//配置多个数据源
		dynamicDataSource.setTargetDataSources(dataSources);
		return dynamicDataSource;
	}

	/**
	 * Druid属性设置
	 * @param datasource
	 */
	private void setDruidOptions(DruidDataSource datasource){
		datasource.setInitialSize(druidProperties.getInitialSize());
		datasource.setMinIdle(druidProperties.getMinIdle());
		datasource.setMaxActive(druidProperties.getMaxActive());
		datasource.setMaxWait(druidProperties.getMaxWait());
		datasource.setTimeBetweenEvictionRunsMillis(druidProperties.getTimeBetweenEvictionRunsMillis());
		datasource.setMinEvictableIdleTimeMillis(druidProperties.getMinEvictableIdleTimeMillis());
		datasource.setTestWhileIdle(druidProperties.isTestWhileIdle());
		datasource.setTestOnBorrow(druidProperties.isTestOnBorrow());
		datasource.setTestOnReturn(druidProperties.isTestOnReturn());
		datasource.setPoolPreparedStatements(druidProperties.isPoolPreparedStatements());
		datasource.setMaxPoolPreparedStatementPerConnectionSize(druidProperties.getMaxPoolPreparedStatementPerConnectionSize());
		try {
			datasource.setFilters(druidProperties.getFilters());
		} catch (SQLException e) {
			log.error("druid configuration initialization filter Exception", e);
		}
		datasource.setConnectionProperties(druidProperties.getConnectionProperties());
	}

	/**
	 * 事务管理
	 * @return
	 */
	@Bean
	public PlatformTransactionManager txManager() {
		return new DataSourceTransactionManager(dataSource());
	}

	/**
	 * 配置Druid监控 http://localhost:15998/druid/datasource.html
	 * @return
	 */
	@Bean(name="druidServlet")
	public ServletRegistrationBean druidServlet() {
		ServletRegistrationBean reg = new ServletRegistrationBean();
		reg.setServlet(new StatViewServlet());

		reg.addUrlMappings("/druid/*");
		reg.addInitParameter("allow", ""); // 白名单
		return reg;
	}

	/**
	 * 设置过滤器
	 * @return
	 */
	@Bean(name = "filterRegistrationBean")
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
		filterRegistrationBean.setFilter(new WebStatFilter());
		filterRegistrationBean.addUrlPatterns("/*");
		filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
		filterRegistrationBean.addInitParameter("profileEnable", "true");
		filterRegistrationBean.addInitParameter("DruidWebStatFilter","/*");
		return filterRegistrationBean;
	}

	/**
	 * 初始化Druid信息
	 * @param url
	 * @param username
	 * @param password
	 * @param driverName
	 * @param validationQuery
	 * @return
	 */
	private DruidDataSource initDruidDataSource(String url,String username,String password,String driverName,String validationQuery){
		DruidDataSource datasource = new DruidDataSource();
		datasource.setUrl(url);//数据库地址
		datasource.setUsername(username);//用户名
		datasource.setPassword(password);//密码
		datasource.setDriverClassName(driverName);//设置驱动
		datasource.setValidationQuery(validationQuery);//检查语句
		setDruidOptions(datasource); // 设置druid数据源的属性
		return datasource;
	}

	/**
	 * 获取配置信息
	 * @param dataName
	 * @param propName
	 * @return
	 */
	private String getDataProp(String dataName,String propName){
		try {
			//查找指定配置信息custom\.datasource\.muti\.datas\.
			if (springContextUtils.containProperty(DATAS_PRE_FIX_DOT+dataName+"."+propName,true)){
				return springContextUtils.getProperty(DATAS_PRE_FIX_DOT+dataName+"."+propName).toString();
			}else{
				throw new RuntimeException(String.format("数据库 %s 缺少属性 %s ",dataName,propName));
			}
		}catch (Exception ex){
			throw new RuntimeException(String.format("数据库 %s 配置信息获取失败 ：%s",dataName,ex.getMessage()));
		}
	}
}